//+------------------------------------------------------------------+
//|                                               RiskManager.mqh    |
//|                                    Copyright 2024, Precision EA  |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Precision EA"
#property strict

#include "Logger.mqh"
#include <Trade/Trade.mqh>
#include <Trade/PositionInfo.mqh>
#include <Trade/AccountInfo.mqh>

class RiskManager {
private:
    string m_symbol;
    double m_account_balance;
    double m_account_equity;
    double m_point;
    double m_tick_value;
    
    // Risk parameters
    struct RiskConfig {
        double risk_per_trade;          // % per trade (e.g., 1.0)
        double max_daily_risk;          // % daily max
        double max_daily_loss;          // % daily loss limit
        double max_position_risk;       // % max per position
        int max_consecutive_losses;     // Max consecutive losses
        int max_daily_trades;           // Max trades per day
        int max_open_positions;         // Max simultaneous positions
        double min_risk_reward;         // Minimum R:R ratio
        bool use_martingale;            // Martingale strategy
        double martingale_multiplier;   // Lot size multiplier
    } m_config;
    
    // Daily tracking
    struct DailyStats {
        datetime date;
        int trades_count;
        int win_count;
        int loss_count;
        double total_profit;
        double total_loss;
        double max_drawdown;
        double current_drawdown;
        int consecutive_losses;
    } m_daily;
    
    // Position tracking
    struct PositionRecord {
        ulong ticket;
        double open_price;
        double volume;
        double risk_amount;
        datetime open_time;
        string symbol;
    };
    
    PositionRecord m_open_positions[100];
    int m_position_count;
    
    void UpdateAccountInfo() {
        m_account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
        m_account_equity = AccountInfoDouble(ACCOUNT_EQUITY);
        
        // Reset daily stats if new day
        MqlDateTime today, last;
        TimeCurrent(today);
        TimeToStruct(m_daily.date, last);
        
        if(today.day != last.day || today.mon != last.mon || today.year != last.year) {
            ResetDailyStats();
        }
        
        // Update drawdown
        double current_dd = (m_account_balance - m_account_equity) / m_account_balance * 100;
        m_daily.current_drawdown = current_dd;
        m_daily.max_drawdown = MathMax(m_daily.max_drawdown, current_dd);
    }
    
    void ResetDailyStats() {
        m_daily.date = TimeCurrent();
        m_daily.trades_count = 0;
        m_daily.win_count = 0;
        m_daily.loss_count = 0;
        m_daily.total_profit = 0;
        m_daily.total_loss = 0;
        m_daily.max_drawdown = 0;
        m_daily.current_drawdown = 0;
        m_daily.consecutive_losses = 0;
        
        Logger::Info("Daily stats reset", "RiskManager");
    }
    
    double CalculatePositionSize(double entry, double sl, double risk_amount) {
        if(entry <= 0 || sl <= 0 || risk_amount <= 0) {
            Logger::Error("Invalid inputs for position size calculation", "RiskManager");
            return 0.0;
        }
        
        double risk_points = MathAbs(entry - sl) / m_point;
        if(risk_points <= 0) {
            Logger::Error("Zero or negative risk points", "RiskManager");
            return 0.0;
        }
        
        double tick_value = SymbolInfoDouble(m_symbol, SYMBOL_TRADE_TICK_VALUE);
        if(tick_value <= 0) {
            // Fallback calculation
            tick_value = SymbolInfoDouble(m_symbol, SYMBOL_TRADE_TICK_SIZE) * 
                        SymbolInfoDouble(m_symbol, SYMBOL_TRADE_CONTRACT_SIZE);
        }
        
        double value_per_point = tick_value / SymbolInfoDouble(m_symbol, SYMBOL_TRADE_TICK_SIZE) * m_point;
        
        if(value_per_point <= 0) {
            Logger::Error("Invalid value per point", "RiskManager");
            return 0.0;
        }
        
        double volume = risk_amount / (risk_points * value_per_point);
        
        // Apply lot size constraints
        double min_lot = SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_MIN);
        double max_lot = SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_MAX);
        double lot_step = SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_STEP);
        
        if(lot_step > 0) {
            volume = MathRound(volume / lot_step) * lot_step;
        }
        
        volume = MathMax(volume, min_lot);
        volume = MathMin(volume, max_lot);
        
        // Check margin requirements
        double margin_required = volume * SymbolInfoDouble(m_symbol, SYMBOL_MARGIN_INITIAL);
        double free_margin = AccountInfoDouble(ACCOUNT_FREEMARGIN);
        
        if(margin_required > free_margin * 0.8) { // Use max 80% of free margin
            volume = (free_margin * 0.8) / SymbolInfoDouble(m_symbol, SYMBOL_MARGIN_INITIAL);
            
            if(lot_step > 0) {
                volume = MathFloor(volume / lot_step) * lot_step;
            }
            volume = MathMax(volume, min_lot);
            
            Logger::Warn(StringFormat("Reduced position size due to margin: %.2f lots", volume), "RiskManager");
        }
        
        return volume;
    }
    
    bool CheckDailyLimits() {
        // Check daily trade limit
        if(m_config.max_daily_trades > 0 && 
           m_daily.trades_count >= m_config.max_daily_trades) {
            Logger::Warn("Daily trade limit reached", "RiskManager");
            return false;
        }
        
        // Check daily loss limit
        double daily_pnl = m_daily.total_profit + m_daily.total_loss;
        double max_daily_loss = m_account_balance * (m_config.max_daily_loss / 100.0);
        
        if(daily_pnl < -max_daily_loss) {
            Logger::Warn(StringFormat("Daily loss limit reached: %.2f", daily_pnl), "RiskManager");
            return false;
        }
        
        // Check daily risk limit
        double daily_risk_used = m_daily.total_loss; // Simplified
        double max_daily_risk = m_account_balance * (m_config.max_daily_risk / 100.0);
        
        if(daily_risk_used > max_daily_risk) {
            Logger::Warn("Daily risk limit reached", "RiskManager");
            return false;
        }
        
        // Check max consecutive losses
        if(m_config.max_consecutive_losses > 0 && 
           m_daily.consecutive_losses >= m_config.max_consecutive_losses) {
            Logger::Warn(StringFormat("Max consecutive losses reached: %d", 
                m_daily.consecutive_losses), "RiskManager");
            return false;
        }
        
        return true;
    }
    
    bool CheckPositionLimits() {
        if(m_config.max_open_positions > 0 && 
           m_position_count >= m_config.max_open_positions) {
            Logger::Warn(StringFormat("Max open positions reached: %d", m_position_count), "RiskManager");
            return false;
        }
        
        // Check for existing position on same symbol
        for(int i = 0; i < m_position_count; i++) {
            if(m_open_positions[i].symbol == m_symbol) {
                Logger::Warn("Position already open on this symbol", "RiskManager");
                return false;
            }
        }
        
        return true;
    }
    
    void UpdatePositionTracking() {
        // Clean up closed positions
        for(int i = m_position_count - 1; i >= 0; i--) {
            if(!PositionSelectByTicket(m_open_positions[i].ticket)) {
                // Position closed, remove from tracking
                for(int j = i; j < m_position_count - 1; j++) {
                    m_open_positions[j] = m_open_positions[j + 1];
                }
                m_position_count--;
            }
        }
        
        // Add new positions
        int total = PositionsTotal();
        for(int i = 0; i < total; i++) {
            ulong ticket = PositionGetTicket(i);
            bool found = false;
            
            for(int j = 0; j < m_position_count; j++) {
                if(m_open_positions[j].ticket == ticket) {
                    found = true;
                    break;
                }
            }
            
            if(!found && m_position_count < ArraySize(m_open_positions)) {
                m_open_positions[m_position_count].ticket = ticket;
                m_open_positions[m_position_count].open_price = PositionGetDouble(POSITION_PRICE_OPEN);
                m_open_positions[m_position_count].volume = PositionGetDouble(POSITION_VOLUME);
                m_open_positions[m_position_count].symbol = PositionGetString(POSITION_SYMBOL);
                m_open_positions[m_position_count].open_time = (datetime)PositionGetInteger(POSITION_TIME);
                m_position_count++;
            }
        }
    }

public:
    RiskManager(string symbol = "") : m_position_count(0) {
        m_symbol = (symbol == "") ? _Symbol : symbol;
        m_point = SymbolInfoDouble(m_symbol, SYMBOL_POINT);
        m_tick_value = SymbolInfoDouble(m_symbol, SYMBOL_TRADE_TICK_VALUE);
        
        // Default configuration
        m_config.risk_per_trade = 1.0;          // 1% per trade
        m_config.max_daily_risk = 5.0;          // 5% daily max
        m_config.max_daily_loss = 3.0;          // 3% daily loss limit
        m_config.max_position_risk = 2.0;       // 2% max per position
        m_config.max_consecutive_losses = 3;    // Max 3 consecutive losses
        m_config.max_daily_trades = 5;          // Max 5 trades per day
        m_config.max_open_positions = 1;        // Max 1 simultaneous position
        m_config.min_risk_reward = 1.5;         // Minimum 1:1.5 R:R
        m_config.use_martingale = false;        // No martingale
        m_config.martingale_multiplier = 2.0;   // Default multiplier
        
        ResetDailyStats();
        UpdateAccountInfo();
        
        Logger::Info("RiskManager initialized", "RiskManager");
    }
    
    void SetConfig(const RiskConfig &config) {
        m_config = config;
        Logger::Info("Risk configuration updated", "RiskManager");
    }
    
    struct RiskAssessment {
        bool allowed;
        double position_size;
        double risk_amount;
        double max_profit;
        double max_loss;
        string reason;
        double risk_reward_ratio;
    };
    
    RiskAssessment AssessTrade(double entry_price, 
                              double stop_loss, 
                              double take_profit,
                              double confidence = 1.0) {
        RiskAssessment assessment = {};
        assessment.allowed = false;
        
        UpdateAccountInfo();
        UpdatePositionTracking();
        
        // Check basic validity
        if(entry_price <= 0 || stop_loss <= 0) {
            assessment.reason = "Invalid price levels";
            return assessment;
        }
        
        // Check daily limits
        if(!CheckDailyLimits()) {
            assessment.reason = "Daily limits exceeded";
            return assessment;
        }
        
        // Check position limits
        if(!CheckPositionLimits()) {
            assessment.reason = "Position limits exceeded";
            return assessment;
        }
        
        // Calculate risk amount based on confidence
        double base_risk = m_account_balance * (m_config.risk_per_trade / 100.0);
        double adjusted_risk = base_risk * confidence;
        
        // Apply martingale if enabled and we have consecutive losses
        if(m_config.use_martingale && m_daily.consecutive_losses > 0) {
            adjusted_risk *= MathPow(m_config.martingale_multiplier, m_daily.consecutive_losses);
            Logger::Warn(StringFormat("Martingale applied: %.2fx", 
                MathPow(m_config.martingale_multiplier, m_daily.consecutive_losses)), "RiskManager");
        }
        
        // Cap risk at maximum per position
        double max_risk = m_account_balance * (m_config.max_position_risk / 100.0);
        adjusted_risk = MathMin(adjusted_risk, max_risk);
        
        // Calculate position size
        double volume = CalculatePositionSize(entry_price, stop_loss, adjusted_risk);
        if(volume <= 0) {
            assessment.reason = "Invalid position size";
            return assessment;
        }
        
        // Calculate risk/reward ratio
        double risk = MathAbs(entry_price - stop_loss);
        double reward = MathAbs(entry_price - take_profit);
        assessment.risk_reward_ratio = (risk > 0) ? reward / risk : 0;
        
        if(assessment.risk_reward_ratio < m_config.min_risk_reward) {
            assessment.reason = StringFormat("Risk/Reward ratio too low: %.2f", 
                assessment.risk_reward_ratio);
            return assessment;
        }
        
        // Calculate monetary values
        assessment.risk_amount = adjusted_risk;
        assessment.position_size = volume;
        assessment.max_loss = adjusted_risk;
        assessment.max_profit = adjusted_risk * assessment.risk_reward_ratio;
        assessment.allowed = true;
        assessment.reason = "Trade approved";
        
        Logger::Info(StringFormat("Trade approved: Size=%.2f, Risk=$%.2f, R:R=%.2f", 
            volume, adjusted_risk, assessment.risk_reward_ratio), "RiskManager");
        
        return assessment;
    }
    
    void RecordTradeResult(ulong ticket, double profit_loss) {
        UpdateAccountInfo();
        
        m_daily.trades_count++;
        
        if(profit_loss > 0) {
            m_daily.win_count++;
            m_daily.total_profit += profit_loss;
            m_daily.consecutive_losses = 0;
        } else {
            m_daily.loss_count++;
            m_daily.total_loss += profit_loss;
            m_daily.consecutive_losses++;
        }
        
        // Remove from open positions
        for(int i = 0; i < m_position_count; i++) {
            if(m_open_positions[i].ticket == ticket) {
                for(int j = i; j < m_position_count - 1; j++) {
                    m_open_positions[j] = m_open_positions[j + 1];
                }
                m_position_count--;
                break;
            }
        }
        
        Logger::Info(StringFormat("Trade recorded: %s $%.2f (Daily: %d/%d)", 
            profit_loss > 0 ? "WIN" : "LOSS", 
            profit_loss, 
            m_daily.trades_count, 
            m_config.max_daily_trades), "RiskManager");
    }
    
    bool IsTradingAllowed() {
        UpdateAccountInfo();
        UpdatePositionTracking();
        
        if(!CheckDailyLimits()) return false;
        if(!CheckPositionLimits()) return false;
        
        // Check account health
        double margin_level = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
        if(margin_level > 0 && margin_level < 100) {
            Logger::Warn(StringFormat("Margin level too low: %.1f%%", margin_level), "RiskManager");
            return false;
        }
        
        // Check free margin
        double free_margin = AccountInfoDouble(ACCOUNT_FREEMARGIN);
        if(free_margin < m_account_balance * 0.1) { // Less than 10% free margin
            Logger::Warn("Insufficient free margin", "RiskManager");
            return false;
        }
        
        return true;
    }
    
    string GetStatus() const {
        string status = "=== RISK MANAGER STATUS ===\n";
        status += StringFormat("Account: $%.2f | Equity: $%.2f | Margin: %.1f%%\n", 
            m_account_balance, m_account_equity, 
            AccountInfoDouble(ACCOUNT_MARGIN_LEVEL));
        status += StringFormat("Daily P&L: $%.2f | Drawdown: %.2f%%\n", 
            m_daily.total_profit + m_daily.total_loss, m_daily.current_drawdown);
        status += StringFormat("Trades Today: %d/%d (W:%d L:%d)\n", 
            m_daily.trades_count, m_config.max_daily_trades, 
            m_daily.win_count, m_daily.loss_count);
        status += StringFormat("Consecutive Losses: %d/%d\n", 
            m_daily.consecutive_losses, m_config.max_consecutive_losses);
        status += StringFormat("Open Positions: %d/%d\n", 
            m_position_count, m_config.max_open_positions);
        status += "===========================\n";
        
        return status;
    }
    
    void PrintStatus() const {
        Print(GetStatus());
    }
    
    double GetDailyProfit() const { return m_daily.total_profit; }
    double GetDailyLoss() const { return m_daily.total_loss; }
    int GetDailyTrades() const { return m_daily.trades_count; }
    double GetDrawdown() const { return m_daily.current_drawdown; }
};